import unit.*;

import java.util.*;

import static java.lang.Math.max;

public class Analysis {
    private static final Analysis analysis = new Analysis();

    //存储中间代码
    private final ArrayList<Quaternion> quaternionList = new ArrayList<>();
    //词法分析结果
    private ArrayList<Word> wordList;
    //储存错误信息
    private final ArrayList<Pair<Integer, String>> errorList = new ArrayList<>();
    //管理变量
    private final Stack<Map<String, Variable>> blocks = new Stack<>();
    private final Map<String, Stack<Map<String, Variable>>> variableTable = new HashMap<>();
    private Map<String, Variable> currentBlock;
    //储存常量的值
    private final Map<String, ArrayList<Integer>> constTable = new HashMap<>();
    //储存函数信息
    private final Map<String, Function> funcTable = new HashMap<>();
    private Function currentFunc;
    //临时变量编号
    private int tmp_id = 0;
    private final Map<String, Integer> varId = new HashMap<>();
    private int jr_id = 0;
    private int ret_id = 0;
    private int param_id = 0;
    private int if_id = -1;
    private int while_id = -1;
    private int currentWhile = 0;
    //储存二维数组第二维长度，以供代码生成阶段使用
    private Map<String, Integer> twoDTable = new HashMap<>();
    //记录是否在main函数中
    private boolean isInMainFunc = false;
    //记录当前函数需保存的参数(解决递归函数bug)
    private ArrayList<String> currentVarTable = new ArrayList<>();

    private Analysis() {
    }

    public static Analysis getInstance() {
        return analysis;
    }

    public ArrayList<Quaternion> getQuaternionList() {
        return quaternionList;
    }

    public ArrayList<Pair<Integer, String>> getErrorList() {
        return errorList;
    }

    public Map<String, Integer> getTwoDTable() {
        return twoDTable;
    }

    public void analysisStart(Node root, ArrayList<Word> wordList) {
        this.wordList = wordList;
        compUnit(root);
    }

    public void compUnit(Node node) {
        currentBlock = new HashMap<>();
        blocks.push(currentBlock);
        ArrayList<Node> children = node.getChildren();
        for (int i = 0; i < children.size(); i++) {
            if (children.get(i).getType().equals("ConstDecl")) {
                constDecl(children.get(i));
            } else if (children.get(i).getType().equals("VarDecl")) {
                varDecl(children.get(i));
            } else if (children.get(i).getType().equals("FuncDef")) {
                funcDef(children.get(i));
            } else {
                mainFuncDef(children.get(i));
            }
        }
    }

    public void constDecl(Node node) {
        ArrayList<Node> children = node.getChildren();
        constDef(children.get(2));
        int index = 3;
        while (children.get(index).getType().equals(",")) {
            constDef(children.get(index + 1));
            index += 2;
        }
    }

    public void constDef(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 3) {
            String name = children.get(0).getType();
            String var = buildVar(name, true, -1, -1, children.get(0).getIndex());
            ArrayList<Integer> nums = constInitVal(children.get(2));
            constTable.put(var, nums);
            quaternionList.add(new Quaternion("=", var, String.valueOf(nums.get(0))));
        } else if (children.size() == 6) {
            String name = children.get(0).getType();
            int a = constExp(children.get(2));
            String var = buildVar(name, true, a, -1, children.get(0).getIndex());
            ArrayList<Integer> nums = constInitVal(children.get(5));
            constTable.put(var, nums);
            quaternionList.add(new Quaternion("create point", var, String.valueOf(a)));

            for (int i = 0; i < a; i++) {
                quaternionList.add(new Quaternion("=", var + "[" + i + "]", String.valueOf(nums.get(i))));
            }
        } else if (children.size() == 9) {
            String name = children.get(0).getType();
            int a = constExp(children.get(2));
            int b = constExp(children.get(5));
            String var = buildVar(name, true, a, b, children.get(0).getIndex());
            ArrayList<Integer> nums = constInitVal(children.get(8));
            constTable.put(var, nums);
            twoDTable.put(var, b);
            quaternionList.add(new Quaternion("create point", var, String.valueOf(a * b)));

            for (int i = 0; i < a; i++) {
                for (int j = 0; j < b; j++) {
                    quaternionList.add(new Quaternion("=", var + "[" +
                            i + "]" + "[" + j + "]", String.valueOf(nums.get(i * b + j))));
                }
            }
        }
    }

    public ArrayList<Integer> constInitVal(Node node) {
        ArrayList<Node> children = node.getChildren();
        ArrayList<Integer> nums = new ArrayList<>();

        if (children.size() == 1) {
            nums.add(constExp(children.get(0)));
        } else {
            for (int i = 0; i < children.size(); i++) {
                Node child = children.get(i);
                if (child.getType().equals("ConstInitVal")) {
                    nums.addAll(constInitVal(child));
                }
            }
        }
        return nums;
    }

    public int constExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        return addConstExp(children.get(0));
    }

    public void varDecl(Node node) {
        ArrayList<Node> children = node.getChildren();
        varDef(children.get(1));
        int index = 2;
        while (children.get(index).getType().equals(",")) {
            varDef(children.get(index + 1));
            index += 2;
        }
    }

    public void varDef(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() <= 3) {
            String name = children.get(0).getType();
            String var = buildVar(name, false, -1, -1, children.get(0).getIndex());
            if (!isInMainFunc) {
                currentVarTable.add(var);
            }
            if (children.get(children.size() - 1).getType().equals("InitVal")) {
                quaternionList.add(new Quaternion("=", var, initVal(children.get(2)).get(0)));
            } else {
                quaternionList.add(new Quaternion("=", var, "0"));
            }
        } else if (children.size() == 5) {
            String name = children.get(0).getType();
            String var = buildVar(name, false, -1, -1, children.get(0).getIndex());
            if (!isInMainFunc) {
                currentVarTable.add(var);
            }
            quaternionList.add(new Quaternion("getint", "read"));
            quaternionList.add(new Quaternion("=", var, "read"));
        } else if (children.size() == 4 || children.size() == 6) {
            String name = children.get(0).getType();
            int a = constExp(children.get(2));
            String var = buildVar(name, false, a, -1, children.get(0).getIndex());
            quaternionList.add(new Quaternion("create point", var, String.valueOf(a)));
            if (children.size() == 6) {
                ArrayList<String> nums = initVal(children.get(5));
                for (int i = 0; i < a; i++) {
                    quaternionList.add(new Quaternion("=", var + "[" + i + "]", nums.get(i)));
                }
            }
        } else if (children.size() == 7 || children.size() == 9) {
            String name = children.get(0).getType();
            int a = constExp(children.get(2));
            int b = constExp(children.get(5));
            String var = buildVar(name, false, a, b, children.get(0).getIndex());
            quaternionList.add(new Quaternion("create point", var, String.valueOf(a * b)));
            twoDTable.put(var, b);
            if (children.size() == 9) {
                ArrayList<String> nums = initVal(children.get(8));
                for (int i = 0; i < a; i++) {
                    for (int j = 0; j < b; j++) {
                        quaternionList.add(new Quaternion("=", var + "[" +
                                i + "]" + "[" + j + "]", nums.get(i * b + j)));
                    }
                }
            }
        }
    }

    public ArrayList<String> initVal(Node node) {
        ArrayList<Node> children = node.getChildren();
        ArrayList<String> nums = new ArrayList<>();
        if (children.size() == 1) {
            nums.add(exp(children.get(0)));
        } else {
            for (int i = 0; i < children.size(); i++) {
                if (children.get(i).getType().equals("InitVal")) {
                    nums.addAll(initVal(children.get(i)));
                }
            }
        }
        return nums;
    }

    public String buildVar(String name, boolean isConst, int a, int b, int index) {
        String var;
        if (variableTable.containsKey(name)) {
            if (currentBlock.containsKey(name)) {
                error(getRow(index), "b");
                return "defineAgain";
            }
            var = "var_" + name + "_" + varId.get(name);
            varId.replace(name, varId.get(name) + 1);
            currentBlock.put(name, new Variable(var, isConst, a, b));
            variableTable.get(name).push(currentBlock);
        } else {
            var = "var_" + name + "_" + 0;
            currentBlock.put(name, new Variable(var, isConst, a, b));
            Stack<Map<String, Variable>> stack = new Stack<>();
            stack.push(currentBlock);
            variableTable.put(name, stack);
            varId.put(name, 1);
        }
        return var;
    }

    public String exp(Node node) {
        ArrayList<Node> children = node.getChildren();
        return addExp(children.get(0));
    }

    public void funcDef(Node node) {
        ArrayList<Node> children = node.getChildren();
        String name = children.get(1).getType();
        Function func = new Function("null", "void");
        currentVarTable = new ArrayList<>();
        if (funcTable.containsKey(name) || currentBlock.containsKey(name)) {
            error(getRow(children.get(1).getIndex()), "b");
        } else {
            func = new Function(name, children.get(0).getChildren().get(0).getType());
            funcTable.put(name, func);
        }

        currentFunc = func;
        quaternionList.add(new Quaternion("label", "func_" + name));
        int ra = jr_id++;
        quaternionList.add(new Quaternion("=", "back_" + ra, "$ra"));
        currentVarTable.add("back_" + ra);
        currentBlock = new HashMap<>();
        blocks.push(currentBlock);
        if (children.size() == 6) {
            funcFParams(children.get(3));
        }
        blockAfter(children.get(children.size() - 1));

        quaternionList.add(new Quaternion("label", "funcEnd_" + name));
        quaternionList.add(new Quaternion("=", "$ra", "back_" + ra));
        quaternionList.add(new Quaternion("jr", "$ra"));
    }

    //bug解决:函数的作用域建立在block之前
    public void blockAfter(Node node) {
        ArrayList<Node> children = node.getChildren();
        for (int i = 1; i < children.size() - 1; i++) {
            if (children.get(i).getType().equals("Stmt")) {
                stmt(children.get(i));
            } else if (children.get(i).getType().equals("VarDecl")) {
                varDecl(children.get(i));
            } else {
                constDecl(children.get(i));
            }
        }
        blocks.pop();
        for (String name : currentBlock.keySet()) {
            variableTable.get(name).pop();
        }
        currentBlock = blocks.peek();
    }

    public void funcFParams(Node node) {
        ArrayList<Node> children = node.getChildren();
        for (int index = 0; index < children.size(); index += 2) {
            funcFParam(children.get(index), index / 2);
        }
    }

    public void funcFParam(Node node, int index) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 2) {
            String name = children.get(1).getType();
            String var = buildVar(name, false, -1, -1, children.get(0).getIndex());
            currentVarTable.add(var);
            quaternionList.add(new Quaternion("=", var, "param_" + index));
            currentFunc.addParam(0);
        } else if (children.size() == 4) {
            String name = children.get(1).getType();
            String var = buildVar(name, false, 0, -1, children.get(0).getIndex());
            currentVarTable.add(var);
            quaternionList.add(new Quaternion("load point", var, "param_" + index, "0"));
            currentFunc.addParam(1);
        } else if (children.size() == 7) {
            String name = children.get(1).getType();
            int b = constExp(children.get(5));
            String var = buildVar(name, false, 0,
                    constExp(children.get(5)), children.get(0).getIndex());
            currentVarTable.add(var);
            quaternionList.add(new Quaternion("load point", var, "param_" + index, "0"));
            twoDTable.put(var, b);
            currentFunc.addParam(2);
        }
    }


    public void mainFuncDef(Node node) {
        isInMainFunc = true;
        currentVarTable = new ArrayList<>();
        ArrayList<Node> children = node.getChildren();
        quaternionList.add(new Quaternion("label", "MainFunction"));
        block(children.get(4));
        isInMainFunc = false;
        quaternionList.add(new Quaternion("label", "mainEnd"));
        quaternionList.add(new Quaternion("end", "0"));
    }

    public void block(Node node) {
        ArrayList<Node> children = node.getChildren();
        currentBlock = new HashMap<>();
        blocks.push(currentBlock);
        for (int i = 1; i < children.size() - 1; i++) {
            if (children.get(i).getType().equals("Stmt")) {
                stmt(children.get(i));
            } else if (children.get(i).getType().equals("VarDecl")) {
                varDecl(children.get(i));
            } else {
                constDecl(children.get(i));
            }
        }
        blocks.pop();
        for (String name : currentBlock.keySet()) {
            variableTable.get(name).pop();
        }
        currentBlock = blocks.peek();
    }

    //隐蔽bug解决："LVal ="语句与变量定义的区别
    public void lValDef(Node node) {
        ArrayList<Node> children = node.getChildren();
        String var = lVal(children.get(0));
        String name;
        if (var.contains("[")) {
            name = var.substring(0, var.indexOf("["));
        } else {
            name = var;
        }
        if (constTable.containsKey(name)) {
            error(getRow(children.get(0).getIndex()), "h");
        }

        if (children.get(2).getType().equals("Exp")) {
            quaternionList.add(new Quaternion("=", var,
                    exp(children.get(2))));
        } else {
            quaternionList.add(new Quaternion("getint", "read"));
            quaternionList.add(new Quaternion("=", var, "read"));
        }
    }

    public void stmt(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.get(0).getType().equals("if")) {
            if_id++;
            int this_if_id = if_id;
            String condition = cond(children.get(2));
            quaternionList.add(new Quaternion("beqz", condition, "ifEnd_" + this_if_id));
            stmt(children.get(4));
            if (children.size() > 5) {
                quaternionList.add(new Quaternion("j", "elseEnd_" + this_if_id));
            }
            quaternionList.add(new Quaternion("label", "ifEnd_" + this_if_id));
            if (children.size() > 5) {
                stmt(children.get(6));
                quaternionList.add(new Quaternion("label", "elseEnd_" + this_if_id));
            }
        } else if (children.get(0).getType().equals("while")) {
            while_id++;
            int this_while_id = while_id;
            quaternionList.add(new Quaternion("label", "while_" + this_while_id));
            String condition = cond(children.get(2));
            quaternionList.add(new Quaternion("beqz", condition, "whileEnd_" + this_while_id));
            int preWhile = currentWhile;
            currentWhile = this_while_id;
            stmt(children.get(4));
            currentWhile = preWhile;
            quaternionList.add(new Quaternion("j", "while_" + this_while_id));
            quaternionList.add(new Quaternion("label", "whileEnd_" + this_while_id));
        } else if (children.get(0).getType().equals("continue")) {
            quaternionList.add(new Quaternion("j", "while_" + currentWhile));
        } else if (children.get(0).getType().equals("break")) {
            quaternionList.add(new Quaternion("j", "whileEnd_" + currentWhile));
        } else if (children.get(0).getType().equals("LVal")) {
            lValDef(node);
        } else if (children.get(0).getType().equals("Exp")) {
            exp(children.get(0));
        } else if (children.get(0).getType().equals("return")) {
            if (children.size() == 3) {
                quaternionList.add(new Quaternion("=", "ret", exp(children.get(1))));
            }
            if (!isInMainFunc) {
                quaternionList.add(new Quaternion("j", "funcEnd_" + currentFunc.getName()));
            } else {
                quaternionList.add(new Quaternion("j", "mainEnd"));
            }
        } else if (children.get(0).getType().equals("printf")) {
            String string = Lexer.getInstance().getWordList().get(children.get(2).getIndex()).getMe();
            ArrayList<String> strs = new ArrayList<>();
            int start = 0;
            for (int end = 0; end < string.length() - 1; end += 1) {
                if (end >= string.length() - 2) {
                    strs.add(string.substring(max(start, 1), end + 1));
                } else if (end < string.length() - 2 && string.substring(end, end + 2).equals("%d")) {
                    strs.add(string.substring(max(start, 1), end));
                    start = end + 2;
                    end += 1;
                }
            }
            ArrayList<String> printExp = new ArrayList<>();
            for (int i = 0; i < strs.size(); i++) {
                if (2 * i + 4 < children.size() - 2) {
                    printExp.add(exp(children.get(2 * i + 4)));
                }
            }
            for (int i = 0; i < strs.size(); i++) {
                quaternionList.add(new Quaternion("printString", strs.get(i)));
                if (2 * i + 4 < children.size() - 2) {
                    quaternionList.add(new Quaternion("printInteger", printExp.get(i)));
                }
            }

        } else if (children.get(0).getType().equals(";")) {
            return;
        } else if (children.get(0).getType().equals("Block")) {
            block(children.get(0));
        }
    }

    public String cond(Node node) {
        return lOrExp(node.getChildren().get(0));
    }

    public String addExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return mulExp(children.get(0));
        } else {
            String var = "tmp_" + tmp_id++;
            quaternionList.add(new Quaternion(children.get(1).getType(), var,
                    addExp(children.get(0)), mulExp(children.get(2))));
            return var;
        }
    }

    public int addConstExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return mulConstExp(children.get(0));
        } else {
            if (children.get(1).getType().equals("+")) {
                return addConstExp(children.get(0)) + mulConstExp(children.get(2));
            } else if (children.get(1).getType().equals("-")) {
                return addConstExp(children.get(0)) - mulConstExp(children.get(2));
            }
        }
        return 0;
    }


    public String mulExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return unaryExp(children.get(0));
        } else {
            String var = "tmp_" + tmp_id++;
            quaternionList.add(new Quaternion(children.get(1).getType(), var,
                    mulExp(children.get(0)), unaryExp(children.get(2))));
            return var;
        }
    }

    public int mulConstExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return unaryConstExp(children.get(0));
        } else {
            if (children.get(1).getType().equals("*")) {
                return mulConstExp(children.get(0)) * unaryConstExp(children.get(2));
            } else if (children.get(1).getType().equals("/")) {
                return mulConstExp(children.get(0)) / unaryConstExp(children.get(2));
            } else if (children.get(1).getType().equals("%")) {
                return mulConstExp(children.get(0)) % unaryConstExp(children.get(2));
            }
        }
        return 0;
    }

    public String unaryExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.get(0).getType().equals("PrimaryExp")) {
            return primaryExp(children.get(0));
        } else if (children.get(0).getType().equals("+") || children.get(0).getType().equals("-")) {
            String tmp = "tmp_" + tmp_id++;
            String var = unaryExp(children.get(1));
            quaternionList.add(new Quaternion(children.get(0).getType(), tmp, "0", var));
            return tmp;
        } else if (children.get(0).getType().equals("!")) {
            String tmp = "tmp_" + tmp_id++;
            String var = unaryExp(children.get(1));
            quaternionList.add(new Quaternion(children.get(0).getType(), tmp, var));
            return tmp;
        } else {
            String name = children.get(0).getType();
            if (!funcTable.containsKey(name)) {
                error(getRow(children.get(0).getIndex()), "c");
                return "undefined";
            }

            if (!isInMainFunc) {
                if (quaternionList.get(quaternionList.size() - 1).getOne().startsWith("tmp_")) {
                    currentVarTable.add(quaternionList.get(quaternionList.size() - 1).getOne());
                }
                for (int i = 0; i < currentVarTable.size(); i++) {
                    quaternionList.add(new Quaternion("save", currentVarTable.get(i)));
                }
            }

            ArrayList<Integer> paramsType = new ArrayList<>();
            if (children.size() > 3) {
                paramsType = funcRParams(children.get(2));
            }
            if (funcTable.get(name).getNumberOfParam() != paramsType.size()) {
                error(getRow(children.get(0).getIndex()), "d");
            } else if (!funcTable.get(name).getParamsType().equals(paramsType)) {
                error(getRow(children.get(0).getIndex()), "e");
            }

            quaternionList.add(new Quaternion("jal", "func_" + name));
            quaternionList.add(new Quaternion("=", "ret_" + ret_id, "ret"));

            if (!isInMainFunc) {
                for (int i = currentVarTable.size() - 1; i >= 0; i--) {
                    quaternionList.add(new Quaternion("recover", currentVarTable.get(i)));
                }
            }

            currentVarTable.add("ret_" + ret_id);

            return "ret_" + ret_id++;
        }
    }

    public int unaryConstExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.get(0).getType().equals("PrimaryExp")) {
            return primaryConstExp(children.get(0));
        } else if (children.get(0).getType().equals("+")) {
            return unaryConstExp(children.get(1));
        } else if (children.get(0).getType().equals("-")) {
            return -1 * unaryConstExp(children.get(1));
        }
        return 0;
    }

    //先分别计算每个参数，再统一录入，防止计算时覆盖之前计算结果
    public ArrayList<Integer> funcRParams(Node node) {
        ArrayList<Node> children = node.getChildren();
        ArrayList<Integer> paramsType = new ArrayList<>();
        ArrayList<String> params = new ArrayList<>();
        for (int index = 0; index < children.size(); index += 2) {
            String varOrPoint = exp(children.get(index));
            if (varOrPoint.endsWith("%")) {
                int i = varOrPoint.indexOf("@");
                String name = varOrPoint.substring(0, i);
                int paramType = Integer.parseInt(varOrPoint.substring(i + 1, i + 2));
                String offset = varOrPoint.substring(i + 4, varOrPoint.length() - 1);

                quaternionList.add(new Quaternion("load point", "param_" + index / 2, name, offset));
                quaternionList.add(new Quaternion("save", "param_" + index / 2));
                params.add("param_" + index / 2);
                paramsType.add(paramType);
            } else {
                quaternionList.add(new Quaternion("=", "param_" + index / 2, varOrPoint));
                quaternionList.add(new Quaternion("save", "param_" + index / 2));
                params.add("param_" + index / 2);
                if (judgeRetVoid(children.get(index))) {
                    paramsType.add(-1);
                } else {
                    paramsType.add(0);
                }
            }
        }

        for (int index = params.size() - 1; index >= 0; index -= 1) {
            quaternionList.add(new Quaternion("recover", "param_" + index));
        }
        /*
        for (int i = 0; i < params.size(); i++) {
            if (params.get(i).getTwo().equals("None")) {
                quaternionList.add(new Quaternion("=", "param_" + i, params.get(i).getOne()));
            } else {
                quaternionList.add(new Quaternion("load point",
                        "param_" + i, params.get(i).getOne(), params.get(i).getTwo()));
            }
        }
         */
        return paramsType;
    }

    public boolean judgeRetVoid(Node node) {
        String name = node.getChildren().get(0).getChildren().get(0).getChildren().get(0).
                getChildren().get(0).getType();
        //System.out.print(name + "\n");
        if (!name.equals("PrimaryExp") && !name.equals("+") &&
                !name.equals("-") && !name.equals("!") &&
                !name.equals("UnaryExp") && !name.equals("MulExp") &&
                !name.equals("AddExp")) {
            return funcTable.get(name).getRetType().equals("void");
        }
        return false;
    }


    public String primaryExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 3) {
            return exp(children.get(1));
        } else if (children.get(0).getType().equals("LVal")) {
            return lVal(children.get(0));
        } else {
            return children.get(0).getType();
        }
    }

    public int primaryConstExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 3) {
            return constExp(children.get(1));
        } else if (children.get(0).getType().equals("LVal")) {
            return constLVal(children.get(0));
        } else {
            return Integer.parseInt(children.get(0).getType());
        }
    }

    public String lVal(Node node) {
        ArrayList<Node> children = node.getChildren();
        String name = children.get(0).getType();
        if (!variableTable.containsKey(name) || variableTable.get(name).size() == 0) {
            error(getRow(children.get(0).getIndex()), "c");
            return "undefined";
        }
        Map<String, Variable> map = variableTable.get(name).peek();
        Variable var = map.get(name);

        if (children.size() == 1) {
            if (var.getFirstLength() == -1 && var.getSecondLength() == -1) {
                return var.getName();
            } else if (var.getSecondLength() == -1) {
                return var.getName() + "@1@%0%";
            } else {
                return var.getName() + "@2@%0%";
            }
        } else if (children.size() == 4) {
            if (var.getSecondLength() == -1) {
                return var.getName() + "[" + exp(children.get(2)) + "]";
            } else {
                return var.getName() + "@1@" + "%" + exp(children.get(2)) + "%";
            }
        } else if (children.size() == 7) {
            return var.getName() + "[" + exp(children.get(2)) + "]"
                    + "[" + exp(children.get(5)) + "]";
        }
        return "fuck";
    }

    public int constLVal(Node node) {
        ArrayList<Node> children = node.getChildren();
        String name = children.get(0).getType();
        Map<String, Variable> map = variableTable.get(name).peek();

        if (children.size() == 1) {
            return getConst(map.get(name));
        } else if (children.size() == 4) {
            return getConst(map.get(name), constExp(children.get(2)));
        } else if (children.size() == 7) {
            return getConst(map.get(name), constExp(children.get(2)), constExp(children.get(5)));
        }
        return -1;
    }

    public String relExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return addExp(children.get(0));
        } else {
            String var = "tmp_" + tmp_id++;
            quaternionList.add(new Quaternion(children.get(1).getType(), var,
                    relExp(children.get(0)), addExp(children.get(2))));
            return var;
        }
    }

    public String eqExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            String var = "tmp_" + tmp_id++;
            quaternionList.add(new Quaternion("=", var, relExp(children.get(0))));
            return var;
        } else {
            String var = "tmp_" + tmp_id++;
            quaternionList.add(new Quaternion(children.get(1).getType(), var,
                    eqExp(children.get(0)), relExp(children.get(2))));
            return var;
        }
    }

    public String lAndExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return eqExp(children.get(0));
        } else {
            int end_id = tmp_id++;
            String var = lAndExp(node, "AndEnd_" + end_id);
            quaternionList.add(new Quaternion("label", "AndEnd_" + end_id));
            return var;
        }
    }

    public String lAndExp(Node node, String AndEnd) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return eqExp(children.get(0));
        } else {
            String var = lAndExp(children.get(0), AndEnd);
            quaternionList.add(new Quaternion("beqz", var, AndEnd));
            quaternionList.add(new Quaternion("=", var, eqExp(children.get(2))));
            return var;
        }
    }

    public String lOrExp(Node node) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return lAndExp(children.get(0));
        } else {
            int end_id = tmp_id++;
            String var = lOrExp(node, "OrEnd_" + end_id);
            quaternionList.add(new Quaternion("label", "OrEnd_" + end_id));
            return var;
        }
    }

    public String lOrExp(Node node, String OrEnd) {
        ArrayList<Node> children = node.getChildren();
        if (children.size() == 1) {
            return lAndExp(children.get(0));
        } else {
            String var = lOrExp(children.get(0), OrEnd);
            quaternionList.add(new Quaternion("bnez", var, OrEnd));
            quaternionList.add(new Quaternion("=", var, lAndExp(children.get(2))));
            return var;
        }
    }

    public int getConst(Variable constVar, int a, int b) {
        return constTable.get(constVar.getName()).get(a * constVar.getSecondLength() + b);
    }

    public int getConst(Variable constVar, int a) {
        return constTable.get(constVar.getName()).get(a);
    }

    public int getConst(Variable constVar) {
        return constTable.get(constVar.getName()).get(0);
    }

    public int getRow(int index) {
        return wordList.get(index).getRow();
    }

    public void error(int row, String type) {
        errorList.add(new Pair<>(row, type));
        //System.out.print(row + " " + type + "\n");
    }
}
